<?php
/**
 * 微信公众号日志记录类
 */
class HYWL_Wechat_Logger {
    
    private $log_enabled;
    private $log_file;
    private $max_log_size; // 单位：MB
    
    /**
     * 构造函数
     */
    public function __construct() {
        $options = get_option('hywl_wechat_options', array());
        // 强制启用日志，调试期间
        $this->log_enabled = true; //isset($options['log_enabled']) ? (bool)$options['log_enabled'] : false;
        $this->max_log_size = isset($options['max_log_size']) ? intval($options['max_log_size']) : 5; // 默认5MB
        
        // 设置日志文件路径
        $this->log_file = HYWL_WECHAT_PLUGIN_DIR . 'logs/wechat-' . date('Y-m-d') . '.log';
        
        // 检查日志目录是否存在并可写
        $log_dir = HYWL_WECHAT_PLUGIN_DIR . 'logs';
        if (!file_exists($log_dir)) {
            mkdir($log_dir, 0755, true);
        }
        
        // 记录一条初始化日志，确认日志系统工作正常
        $this->info('日志系统初始化成功 - ' . date('Y-m-d H:i:s'));
    }
    
    /**
     * 写入日志
     * 
     * @param string $message 日志消息
     * @param string $level 日志级别 (info, warning, error)
     * @return bool 是否成功写入
     */
    public function log($message, $level = 'info') {
        // 如果日志功能被禁用，直接返回
        if (!$this->log_enabled) {
            return false;
        }
        
        // 检查日志文件大小，如果超过限制则进行处理
        $this->check_log_size();
        
        // 格式化日志信息
        $log_entry = sprintf(
            "[%s] [%s] %s\n",
            date('Y-m-d H:i:s'),
            strtoupper($level),
            $message
        );
        
        // 写入日志文件
        $result = file_put_contents($this->log_file, $log_entry, FILE_APPEND);
        
        return ($result !== false);
    }
    
    /**
     * 记录信息级别日志
     * 
     * @param string $message 日志消息
     * @return bool 是否成功写入
     */
    public function info($message) {
        return $this->log($message, 'info');
    }
    
    /**
     * 记录警告级别日志
     * 
     * @param string $message 日志消息
     * @return bool 是否成功写入
     */
    public function warning($message) {
        return $this->log($message, 'warning');
    }
    
    /**
     * 记录错误级别日志
     * 
     * @param string $message 日志消息
     * @return bool 是否成功写入
     */
    public function error($message) {
        return $this->log($message, 'error');
    }
    
    /**
     * 检查日志文件大小
     * 如果超过设定的最大大小，则创建新日志文件
     */
    private function check_log_size() {
        if (file_exists($this->log_file)) {
            $size_mb = filesize($this->log_file) / (1024 * 1024);
            if ($size_mb >= $this->max_log_size) {
                // 重命名当前日志文件，添加时间戳
                $timestamp = date('Y-m-d-H-i-s');
                $new_name = HYWL_WECHAT_PLUGIN_DIR . 'logs/wechat-' . date('Y-m-d') . '-' . $timestamp . '.log';
                rename($this->log_file, $new_name);
                
                // 删除过多的旧日志文件
                $this->cleanup_old_logs();
            }
        }
    }
    
    /**
     * 清理旧日志文件
     * 保留最近30个日志文件
     */
    private function cleanup_old_logs() {
        $log_dir = HYWL_WECHAT_PLUGIN_DIR . 'logs';
        $log_files = glob($log_dir . '/wechat-*.log');
        
        // 按文件修改时间排序
        usort($log_files, function($a, $b) {
            return filemtime($b) - filemtime($a);
        });
        
        // 保留最近30个日志文件
        $max_files = 30;
        if (count($log_files) > $max_files) {
            $files_to_delete = array_slice($log_files, $max_files);
            foreach ($files_to_delete as $file) {
                @unlink($file);
            }
        }
    }
    
    /**
     * 获取日志列表
     * 
     * @param int $limit 限制数量
     * @return array 日志文件列表
     */
    public function get_log_files($limit = 10) {
        $log_dir = HYWL_WECHAT_PLUGIN_DIR . 'logs';
        $log_files = glob($log_dir . '/wechat-*.log');
        
        // 按文件修改时间排序
        usort($log_files, function($a, $b) {
            return filemtime($b) - filemtime($a);
        });
        
        // 格式化日志文件信息
        $result = array();
        foreach (array_slice($log_files, 0, $limit) as $file) {
            $result[] = array(
                'file' => basename($file),
                'size' => $this->format_file_size(filesize($file)),
                'date' => date('Y-m-d H:i:s', filemtime($file))
            );
        }
        
        return $result;
    }
    
    /**
     * 格式化文件大小
     * 
     * @param int $bytes 文件大小（字节）
     * @return string 格式化后的大小
     */
    private function format_file_size($bytes) {
        $units = array('B', 'KB', 'MB', 'GB', 'TB');
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        $bytes /= pow(1024, $pow);
        
        return round($bytes, 2) . ' ' . $units[$pow];
    }
    
    /**
     * 获取日志内容
     * 
     * @param string $file 日志文件名
     * @param int $lines 行数
     * @return string 日志内容
     */
    public function get_log_content($file, $lines = 100) {
        $log_dir = HYWL_WECHAT_PLUGIN_DIR . 'logs';
        $file_path = $log_dir . '/' . $file;
        
        // 安全检查，确保只读取logs目录中的文件
        if (!file_exists($file_path) || strpos(realpath($file_path), realpath($log_dir)) !== 0) {
            return '';
        }
        
        // 读取最后N行
        return $this->tail_file($file_path, $lines);
    }
    
    /**
     * 读取文件最后N行
     * 
     * @param string $file 文件路径
     * @param int $lines 行数
     * @return string 文件内容
     */
    private function tail_file($file, $lines = 100) {
        $f = @fopen($file, "r");
        if (!$f) {
            return '';
        }
        
        // 设置较大的缓冲区
        $buffer = 4096;
        fseek($f, 0, SEEK_END);
        $size = ftell($f);
        
        // 如果文件小于缓冲区大小，直接读取整个文件
        if ($size <= $buffer) {
            fseek($f, 0, SEEK_SET);
            $text = fread($f, $size);
            fclose($f);
            return $text;
        }
        
        // 从文件末尾开始，逐块读取
        $text = '';
        $found_lines = 0;
        
        // 循环读取直到找到足够的行数或到达文件开头
        while ($found_lines < $lines && ftell($f) > 0) {
            // 确定要读取的字节数
            $seek_size = min(ftell($f), $buffer);
            fseek($f, -$seek_size, SEEK_CUR);
            $read = fread($f, $seek_size);
            fseek($f, -$seek_size, SEEK_CUR);
            
            // 计算当前块中的行数
            $count = substr_count($read, "\n");
            
            // 如果找到足够的行，取最后N行
            if ($count >= $lines - $found_lines) {
                // 找到最后N行的起始位置
                $line_pos = strrpos($read, "\n", (-1 * strlen($read)) + strrpos($read, "\n") + 1);
                if ($line_pos !== false) {
                    $text = substr($read, $line_pos + 1) . $text;
                } else {
                    $text = $read . $text;
                }
                break;
            } else {
                // 继续向前读取
                $text = $read . $text;
                $found_lines += $count;
            }
        }
        
        fclose($f);
        
        // 只返回最后N行
        $lines_array = explode("\n", $text);
        $lines_array = array_slice($lines_array, -$lines);
        
        return implode("\n", $lines_array);
    }
} 